package cntr.proxies;

import cntr.JGbContainer;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.*;

/**
 * Created by Thanh-Trung Pham.
 */
public class EventChannelProxy implements InvocationHandler {

    private JGbContainer m_oLocalContainer;

    private List<String> m_oSinkContainerNames = new ArrayList<>(); //the names of containers hosting the deployable entities (component instances or channels) playing the sink role.
    private List<String> m_oSinkDeployableEntityNames = new ArrayList<>(); //the name of the deployable entities (component instances or channels) playing the sink role.

    private Map<String, Object> m_oFilters = new HashMap<>();


    /**
     * The constructor.
     */
    public EventChannelProxy(JGbContainer localContainer) {
        m_oLocalContainer = localContainer;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        for (int i = 0; i < m_oSinkContainerNames.size(); i++) {
            String strSinkDeployableEntityName = m_oSinkDeployableEntityNames.get(i);
            Object oFilter = m_oFilters.get(strSinkDeployableEntityName);
            if (oFilter == null) {
                m_oLocalContainer.sendMethodInvocationMessage(m_oSinkContainerNames.get(i), strSinkDeployableEntityName, method, args);
                continue;
            }

            try {
                Method oFilterMethod = oFilter.getClass().getMethod(method.getName(), method.getParameterTypes());
                if (oFilterMethod.getReturnType().equals(boolean.class)) {
                    if (((boolean) oFilterMethod.invoke(oFilter, args))) {
                        m_oLocalContainer.sendMethodInvocationMessage(m_oSinkContainerNames.get(i), strSinkDeployableEntityName, method, args);
                    }
                }
            } catch (NoSuchMethodException oNoSuchMethodException) {
                m_oLocalContainer.sendMethodInvocationMessage(m_oSinkContainerNames.get(i), strSinkDeployableEntityName, method, args);
            }
        }

        return null;
    }

    /**
     * Add a sink container name, a sink deployable entity name, and a filter (if any).
     *
     * @param sinkContainerName        the given sink container name.
     * @param sinkDeployableEntityName the given sink deployable entity name.
     */
    public void addSinkContainerNameSinkDeployableEntityNameAndFilter(String sinkContainerName, String sinkDeployableEntityName, Object filter) {
        m_oSinkContainerNames.add(sinkContainerName);
        m_oSinkDeployableEntityNames.add(sinkDeployableEntityName);
        m_oFilters.put(sinkDeployableEntityName, filter);

    }
}
